--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Node / ReticulumProjects / MeshChatX.git / files / meshchatx / src / backend / http / routes / interfaces.py
Displaying Raw • Download
meshchatx/src/backend/http/routes/interfaces.py b960db2e8bf87b96c14f6421402a16798d7a9ded (b960db2e) Text, 78.64 KB
T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""HTTP routes: interfaces."""
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787httpT7ee787.T7ee787meshchat_names Tff7b72import Tb4b4b4( T8b949e# noqa: F401
Te6edf3GeoValidationErrorTb4b4b4,
Te6edf3OutboundHttpBlockedErrorTb4b4b4,
Te6edf3OverlayExportErrorTb4b4b4,
Te6edf3OverlaySourceParseErrorTb4b4b4,
Te6edf3PluginSecurityErrorTb4b4b4,
Te6edf3AsyncUtilsTb4b4b4,
Te6edf3InterfaceConfigParserTb4b4b4,
Te6edf3InterfaceDiscoveryTb4b4b4,
Te6edf3InterfaceEditorTb4b4b4,
Te6edf3LOGIN_PATHTb4b4b4,
Te6edf3LXMFTb4b4b4,
Te6edf3LxmfAudioFieldTb4b4b4,
Te6edf3LxmfFileAttachmentTb4b4b4,
Te6edf3LxmfFileAttachmentsFieldTb4b4b4,
Te6edf3LxmfImageFieldTb4b4b4,
Te6edf3MAX_EXPORT_TILESTb4b4b4,
Te6edf3MarkdownRendererTb4b4b4,
Te6edf3NomadnetFileDownloaderTb4b4b4,
Te6edf3NomadnetPageDownloaderTb4b4b4,
Te6edf3RNProbeHandlerTb4b4b4,
Te6edf3RNSTb4b4b4,
Te6edf3ReticulumMeshChatTb4b4b4,
Te6edf3SETUP_PATHTb4b4b4,
Te6edf3TRANSPARENT_TILETb4b4b4,
Te6edf3TelemeterTb4b4b4,
Te6edf3UTCTb4b4b4,
Te6edf3WSMsgTypeTb4b4b4,
Te6edf3_is_chaquopy_androidTb4b4b4,
Te6edf3_is_loopback_bind_hostTb4b4b4,
Te6edf3_request_client_ipTb4b4b4,
Te6edf3aiohttpTb4b4b4,
Te6edf3app_versionTb4b4b4,
Te6edf3assert_migration_context_pathsTb4b4b4,
Te6edf3asyncioTb4b4b4,
Te6edf3base64Tb4b4b4,
Te6edf3bcryptTb4b4b4,
Te6edf3binasciiTb4b4b4,
Te6edf3build_blocklist_export_documentTb4b4b4,
Te6edf3build_export_documentTb4b4b4,
Te6edf3build_messages_export_bundleTb4b4b4,
Te6edf3cache_statsTb4b4b4,
Te6edf3cancel_inbound_deliveriesTb4b4b4,
Te6edf3castTb4b4b4,
Te6edf3compute_lxmf_conversation_unread_from_latest_rowTb4b4b4,
Te6edf3configparserTb4b4b4,
Te6edf3contextlibTb4b4b4,
Te6edf3convert_db_favourite_to_dictTb4b4b4,
Te6edf3convert_db_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_nomadnet_field_data_to_mapTb4b4b4,
Te6edf3convert_nomadnet_string_data_to_mapTb4b4b4,
Te6edf3convert_propagation_node_state_to_stringTb4b4b4,
Te6edf3copyTb4b4b4,
Te6edf3datetimeTb4b4b4,
Te6edf3describe_port_conflictTb4b4b4,
Te6edf3detect_image_format_from_magicTb4b4b4,
Te6edf3ensure_outbound_http_allowedTb4b4b4,
Te6edf3ensure_session_csrf_tokenTb4b4b4,
Te6edf3filter_announced_dicts_by_search_queryTb4b4b4,
Te6edf3fresh_storage_at_targetTb4b4b4,
Te6edf3get_cached_active_linkTb4b4b4,
Te6edf3get_file_pathTb4b4b4,
Te6edf3get_sessionTb4b4b4,
Te6edf3get_trusted_proxy_cidrsTb4b4b4,
Te6edf3gif_utilsTb4b4b4,
Te6edf3i2p_supportTb4b4b4,
Te6edf3import_messages_export_bundleTb4b4b4,
Te6edf3ioTb4b4b4,
Te6edf3is_mbtiles_filenameTb4b4b4,
Te6edf3is_path_within_dirTb4b4b4,
Te6edf3is_port_in_useTb4b4b4,
Te6edf3is_user_facing_lxmf_payloadTb4b4b4,
Te6edf3jsonTb4b4b4,
Te6edf3list_host_network_interfacesTb4b4b4,
Te6edf3list_inbound_deliveriesTb4b4b4,
Te6edf3list_portsTb4b4b4,
Te6edf3load_app_security_settingsTb4b4b4,
Te6edf3loggerTb4b4b4,
Te6edf3loggingTb4b4b4,
Te6edf3lxmf_sidebar_preview_for_conversation_latest_rowTb4b4b4,
Te6edf3memory_log_handlerTb4b4b4,
Te6edf3message_fields_have_attachmentsTb4b4b4,
Te6edf3migrate_legacy_to_targetTb4b4b4,
Te6edf3mime_for_image_typeTb4b4b4,
Te6edf3normalize_identity_storage_hashTb4b4b4,
Te6edf3normalize_lxmf_sieve_filtersTb4b4b4,
Te6edf3normalize_message_blocklistTb4b4b4,
Te6edf3osTb4b4b4,
Te6edf3parse_bool_query_paramTb4b4b4,
Te6edf3parse_import_documentTb4b4b4,
Te6edf3parse_lxmf_display_nameTb4b4b4,
Te6edf3parse_lxmf_propagation_node_app_dataTb4b4b4,
Te6edf3parse_lxmf_sieve_filters_jsonTb4b4b4,
Te6edf3parse_lxmf_stamp_costTb4b4b4,
Te6edf3parse_message_blocklist_jsonTb4b4b4,
Te6edf3parse_nomadnetwork_node_display_nameTb4b4b4,
Te6edf3platformTb4b4b4,
Te6edf3privacy_mode_enabledTb4b4b4,
Te6edf3psutilTb4b4b4,
Te6edf3purge_messages_before_cutoffTb4b4b4,
Te6edf3reTb4b4b4,
Te6edf3resolve_message_age_cutoffTb4b4b4,
Te6edf3reticulum_pathfindingTb4b4b4,
Te6edf3rotate_session_csrf_tokenTb4b4b4,
Te6edf3rrc_protocolTb4b4b4,
Te6edf3safe_path_under_dirTb4b4b4,
Te6edf3sanitize_sticker_emojiTb4b4b4,
Te6edf3sanitize_sticker_nameTb4b4b4,
Te6edf3sanitize_websocket_config_updateTb4b4b4,
Te6edf3save_app_security_settingsTb4b4b4,
Te6edf3secretsTb4b4b4,
Te6edf3shutilTb4b4b4,
Te6edf3sqlite3Tb4b4b4,
Te6edf3sticker_pack_utilsTb4b4b4,
Te6edf3sysTb4b4b4,
Te6edf3tempfileTb4b4b4,
Te6edf3threadingTb4b4b4,
Te6edf3timeTb4b4b4,
Te6edf3tracebackTb4b4b4,
Te6edf3user_agent_hashTb4b4b4,
Te6edf3validate_export_documentTb4b4b4,
Te6edf3webTb4b4b4,
Te6edf3websocket_type_requires_authTb4b4b4,
Te6edf3zipfileTb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8ffregister_interfaces_routesTb4b4b4(Te6edf3routesTb4b4b4, Te6edf3appTb4b4b4)Tb4b4b4:
T8b949e# fetch com ports
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/comportsTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffcomportsTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3comports Tff7b72= Tb4b4b4[
Tb4b4b4{
Ta5d6ff"Ta5d6ffdeviceTa5d6ff"Tb4b4b4: Te6edf3comportTff7b72.Td2a8ffdeviceTb4b4b4,
Ta5d6ff"Ta5d6ffproductTa5d6ff"Tb4b4b4: Te6edf3comportTff7b72.Td2a8ffproductTb4b4b4,
Ta5d6ff"Ta5d6ffserial_numberTa5d6ff"Tb4b4b4: Te6edf3comportTff7b72.Td2a8ffserial_numberTb4b4b4,
Tb4b4b4}
Tff7b72for Te6edf3comport Tff7b72in Te6edf3list_portsTff7b72.Td2a8ffcomportsTb4b4b4(Tb4b4b4)
Tb4b4b4]
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffcomportsTa5d6ff"Tb4b4b4: Te6edf3comportsTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/system/network-interfacesTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffsystem_network_interfacesTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3interfacesTb4b4b4, Te6edf3unavailable_reason Tff7b72= Te6edf3list_host_network_interfacesTb4b4b4(Tb4b4b4)
Te6edf3payload Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4: Te6edf3interfacesTb4b4b4,
Ta5d6ff"Ta5d6ffunavailable_reasonTa5d6ff"Tb4b4b4: Te6edf3unavailable_reasonTb4b4b4,
Tb4b4b4}
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Te6edf3payloadTb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/tools/rnode/latest_releaseTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fftools_rnode_latest_releaseTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e"""Proxy GitHub's latest-release JSON for RNode firmware (official repo).
Browsers cannot reliably call third-party APIs from static pages (CORS); the
MeshChat server fetches api.github.com instead. Default repo is
markqvist/RNode_Firmware; optional ?repo=owner/name must match a strict slug.
"""
Te6edf3repo Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffrepoTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffmarkqvist/RNode_FirmwareTa5d6ff"Tb4b4b4)
Tff7b72if Ta5d6ff"Ta5d6ff/Ta5d6ff" Tff7b72not Tff7b72in Te6edf3repo Tff7b72or Tffa657anyTb4b4b4(Te6edf3c Tff7b72in Te6edf3repo Tff7b72for Te6edf3c Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff#Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4, Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid repoTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3reTff7b72.Td2a8fffullmatchTb4b4b4(Ta5d6ffrTa5d6ff"Ta5d6ff[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+Ta5d6ff"Tb4b4b4, Te6edf3repoTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid repoTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Te6edf3url Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffhttps://api.github.com/repos/Tffd700{Te6edf3repoTffd700}Ta5d6ff/releases/latestTa5d6ff"
Te6edf3gh_headers Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffAcceptTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffapplication/vnd.github+jsonTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffX-GitHub-Api-VersionTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff2022-11-28Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffUser-AgentTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffMeshChatX-RNodeFlasherTa5d6ff"Tb4b4b4,
Tb4b4b4}
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_context Tff7b72and Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3ensure_outbound_http_allowedTb4b4b4(
Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTb4b4b4,
Te6edf3featureTff7b72=Ta5d6ff"Ta5d6ffRNode firmware metadata fetchTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3timeout Tff7b72= Te6edf3aiohttpTff7b72.Td2a8ffClientTimeoutTb4b4b4(Te6edf3totalTff7b72=T79c0ff15Tb4b4b4)
Tff7b72async Tff7b72with Te6edf3aiohttpTff7b72.Td2a8ffClientSessionTb4b4b4(Te6edf3timeoutTff7b72=Te6edf3timeoutTb4b4b4) Tff7b72as Te6edf3sessionTb4b4b4:
Tff7b72async Tff7b72with Te6edf3sessionTff7b72.Td2a8ffgetTb4b4b4(
Te6edf3urlTb4b4b4,
Te6edf3headersTff7b72=Te6edf3gh_headersTb4b4b4,
Te6edf3allow_redirectsTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4) Tff7b72as Te6edf3responseTb4b4b4:
Tff7b72if Te6edf3responseTff7b72.Td2a8ffstatus Tff7b72!= T79c0ff200Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to fetch release: Tffd700{Te6edf3responseTff7b72.Td2a8ffstatusTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=Te6edf3responseTff7b72.Td2a8ffstatusTb4b4b4,
Tb4b4b4)
Te6edf3data Tff7b72= Tff7b72await Te6edf3responseTff7b72.Td2a8ffjsonTb4b4b4(Te6edf3content_typeTff7b72=Tff7b72NoneTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Te6edf3dataTb4b4b4)
Tff7b72except Te6edf3OutboundHttpBlockedError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff403Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff500Tb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/tools/rnode/download_firmwareTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fftools_rnode_download_firmwareTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3url Tff7b72= Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffurlTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3urlTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffURL is requiredTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
T8b949e# Restrict to allowed sources for safety
Te6edf3gitea_url Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_context Tff7b72and Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3gitea_url Tff7b72= Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTff7b72.Td2a8ffgitea_base_urlTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)
Te6edf3allowed Tff7b72= Tb4b4b4[
Ta5d6ff"Ta5d6ffhttps://github.com/Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffhttps://codeload.github.com/Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffhttps://objects.githubusercontent.com/Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffhttps://release-assets.githubusercontent.com/Ta5d6ff"Tb4b4b4,
Tb4b4b4]
Tff7b72if Te6edf3gitea_urlTb4b4b4:
Te6edf3allowedTff7b72.Td2a8ffinsertTb4b4b4(T79c0ff0Tb4b4b4, Te6edf3gitea_urlTff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657anyTb4b4b4(Te6edf3urlTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3aTb4b4b4) Tff7b72for Te6edf3a Tff7b72in Te6edf3allowedTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid download URLTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff403Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_context Tff7b72and Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3ensure_outbound_http_allowedTb4b4b4(
Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTb4b4b4,
Te6edf3featureTff7b72=Ta5d6ff"Ta5d6ffRNode firmware downloadTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72async Tff7b72with Te6edf3aiohttpTff7b72.Td2a8ffClientSessionTb4b4b4(Tb4b4b4) Tff7b72as Te6edf3sessionTb4b4b4:
Tff7b72async Tff7b72with Te6edf3sessionTff7b72.Td2a8ffgetTb4b4b4(Te6edf3urlTb4b4b4, Te6edf3allow_redirectsTff7b72=Tff7b72TrueTb4b4b4) Tff7b72as Te6edf3responseTb4b4b4:
Te6edf3final_url Tff7b72= Tffa657strTb4b4b4(Te6edf3responseTff7b72.Td2a8ffurlTb4b4b4)
Tff7b72if Tff7b72not Tffa657anyTb4b4b4(Te6edf3final_urlTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3aTb4b4b4) Tff7b72for Te6edf3a Tff7b72in Te6edf3allowedTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid download redirect URLTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff403Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3responseTff7b72.Td2a8ffstatus Tff7b72!= T79c0ff200Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to download: Tffd700{Te6edf3responseTff7b72.Td2a8ffstatusTffd700}Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=Te6edf3responseTff7b72.Td2a8ffstatusTb4b4b4,
Tb4b4b4)
Te6edf3data Tff7b72= Tff7b72await Te6edf3responseTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)
Te6edf3filename Tff7b72= Te6edf3urlTff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)Tb4b4b4[Tff7b72-T79c0ff1Tb4b4b4]
Tff7b72return Te6edf3webTff7b72.Td2a8ffResponseTb4b4b4(
Te6edf3bodyTff7b72=Te6edf3dataTb4b4b4,
Te6edf3content_typeTff7b72=Ta5d6ff"Ta5d6ffapplication/zipTa5d6ff"Tb4b4b4,
Te6edf3headersTff7b72=Tb4b4b4{
Ta5d6ff"Ta5d6ffContent-DispositionTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff'Ta5d6ffattachment; filename=Ta5d6ff"Tffd700{Te6edf3filenameTffd700}Ta5d6ff"Ta5d6ff'Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Te6edf3OutboundHttpBlockedError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff403Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff500Tb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/tools/micron-parser-go-releaseTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fftools_micron_parser_go_releaseTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e"""Proxy Micron-Parser-Go release files from one fixed GitHub repo (CSP-safe).
Browsers cannot fetch github.com under the default connect-src; the server
fetches only https://github.com/Quad4-Software/Micron-Parser-Go/releases/download/{tag}/{asset}.
"""
Te6edf3tag Tff7b72= Tb4b4b4(Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftagTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3asset Tff7b72= Tb4b4b4(Te6edf3requestTff7b72.Td2a8ffqueryTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffassetTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3allowed_assets Tff7b72= Tffa657frozensetTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffSHASUMS256.txtTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffmicron-parser-go.wasmTa5d6ff"Tb4b4b4}Tb4b4b4)
Tff7b72if Te6edf3asset Tff7b72not Tff7b72in Te6edf3allowed_assetsTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid assetTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3tag Tff7b72or Tffa657lenTb4b4b4(Te6edf3tagTb4b4b4) Tff7b72> T79c0ff128Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid tagTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72if Tffa657anyTb4b4b4(Te6edf3c Tff7b72in Te6edf3tag Tff7b72for Te6edf3c Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4, Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff#Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4, Ta5d6ff"Tffea00\tTa5d6ff"Tb4b4b4, Ta5d6ff"Tffea00\nTa5d6ff"Tb4b4b4, Ta5d6ff"Tffea00\rTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid tagTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3reTff7b72.Td2a8fffullmatchTb4b4b4(Ta5d6ffrTa5d6ff"Ta5d6ffv[A-Za-z0-9._-]+Ta5d6ff"Tb4b4b4, Te6edf3tagTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid tag formatTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Te6edf3upstream Tff7b72= Tb4b4b4(
Ta5d6ff"Ta5d6ffhttps://github.com/Quad4-Software/Micron-Parser-Go/releases/download/Ta5d6ff"
Ta5d6fffTa5d6ff"Tffd700{Te6edf3tagTffd700}Ta5d6ff/Tffd700{Te6edf3assetTffd700}Ta5d6ff"
Tb4b4b4)
Tff7b72if Te6edf3asset Tff7b72== Ta5d6ff"Ta5d6ffSHASUMS256.txtTa5d6ff"Tb4b4b4:
Te6edf3max_bytes Tff7b72= T79c0ff64 Tff7b72* T79c0ff1024
Tff7b72elseTb4b4b4:
Te6edf3max_bytes Tff7b72= T79c0ff20 Tff7b72* T79c0ff1024 Tff7b72* T79c0ff1024
Te6edf3headers_out Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffCache-ControlTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffno-storeTa5d6ff"Tb4b4b4,
Tb4b4b4}
Te6edf3gh_headers Tff7b72= Tb4b4b4{Ta5d6ff"Ta5d6ffUser-AgentTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffMeshChatX-MicronWasmRelease/1.0Ta5d6ff"Tb4b4b4}
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_context Tff7b72and Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3ensure_outbound_http_allowedTb4b4b4(
Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTb4b4b4,
Te6edf3featureTff7b72=Ta5d6ff"Ta5d6ffMicron parser release fetchTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3timeout Tff7b72= Te6edf3aiohttpTff7b72.Td2a8ffClientTimeoutTb4b4b4(Te6edf3totalTff7b72=T79c0ff120Tb4b4b4)
Tff7b72async Tff7b72with Te6edf3aiohttpTff7b72.Td2a8ffClientSessionTb4b4b4(Te6edf3timeoutTff7b72=Te6edf3timeoutTb4b4b4) Tff7b72as Te6edf3sessionTb4b4b4:
Tff7b72async Tff7b72with Te6edf3sessionTff7b72.Td2a8ffgetTb4b4b4(
Te6edf3upstreamTb4b4b4,
Te6edf3headersTff7b72=Te6edf3gh_headersTb4b4b4,
Te6edf3allow_redirectsTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4) Tff7b72as Te6edf3responseTb4b4b4:
Tff7b72if Te6edf3responseTff7b72.Td2a8ffstatus Tff7b72!= T79c0ff200Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffUpstream returned Tffd700{Te6edf3responseTff7b72.Td2a8ffstatusTffd700}Ta5d6ff Ta5d6ff"
Ta5d6fffTa5d6ff"Ta5d6fffor Micron-Parser-Go Tffd700{Te6edf3tagTffd700}Ta5d6ff/Tffd700{Te6edf3assetTffd700}Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff502Tb4b4b4,
Tb4b4b4)
Te6edf3data Tff7b72= Tff7b72await Te6edf3responseTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72> Te6edf3max_bytesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffRelease asset exceeds size limitTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff502Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3asset Tff7b72== Ta5d6ff"Ta5d6ffSHASUMS256.txtTa5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffResponseTb4b4b4(
Te6edf3bodyTff7b72=Te6edf3dataTb4b4b4,
Te6edf3content_typeTff7b72=Ta5d6ff"Ta5d6fftext/plainTa5d6ff"Tb4b4b4,
Te6edf3headersTff7b72=Te6edf3headers_outTb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffResponseTb4b4b4(
Te6edf3bodyTff7b72=Te6edf3dataTb4b4b4,
Te6edf3content_typeTff7b72=Ta5d6ff"Ta5d6ffapplication/wasmTa5d6ff"Tb4b4b4,
Te6edf3headersTff7b72=Te6edf3headers_outTb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff502Tb4b4b4)
T8b949e# fetch reticulum interfaces
T8b949e# fetch reticulum interfaces
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfacesTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interfacesTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_snapshotTb4b4b4(Tb4b4b4)
Te6edf3processed_interfaces Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72for Te6edf3interface_nameTb4b4b4, Te6edf3interface Tff7b72in Te6edf3interfacesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3interface_data Tff7b72= Te6edf3copyTff7b72.Td2a8ffdeepcopyTb4b4b4(Te6edf3interfaceTb4b4b4)
T8b949e# handle sub-interfaces for RNodeMultiInterface
Tff7b72if Te6edf3interface_dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4:
Te6edf3sub_interfaces Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3sub_nameTb4b4b4, Te6edf3sub_config Tff7b72in Te6edf3interface_dataTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3sub_name Tff7b72not Tff7b72in Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffselected_interface_modeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffconfigured_bitrateTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4:
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3sub_configTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3sub_configTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3sub_name
Te6edf3sub_interfacesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3sub_configTb4b4b4)
T8b949e# add sub-interfaces to the main interface data
Te6edf3interface_dataTb4b4b4[Ta5d6ff"Ta5d6ffsub_interfacesTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3sub_interfaces
Tff7b72for Te6edf3sub Tff7b72in Te6edf3sub_interfacesTb4b4b4:
Tff7b72del Te6edf3interface_dataTb4b4b4[Te6edf3subTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]Tb4b4b4]
Te6edf3processed_interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4] Tff7b72= Te6edf3interface_data
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4: Te6edf3processed_interfacesTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/bitratesTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interfaces_bitratesTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e"""Set forced bitrate (bps) on named interfaces and optionally reload RNS."""
Tff7b72tryTb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid JSONTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3dataTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid JSON objectTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Te6edf3bitrates Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffbitratesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3bitratesTb4b4b4, Tffa657dictTb4b4b4) Tff7b72or Tff7b72not Te6edf3bitratesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffbitrates object is requiredTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3reload_stack Tff7b72= Tffa657boolTb4b4b4(Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffreloadTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4)Tb4b4b4)
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_sectionTb4b4b4(Tb4b4b4)
Te6edf3interfaces_before_write Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_snapshotTb4b4b4(Tb4b4b4)
Te6edf3updated Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3missing Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3raw_nameTb4b4b4, Te6edf3raw_bps Tff7b72in Te6edf3bitratesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3name Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffsanitize_interface_section_nameTb4b4b4(Tffa657strTb4b4b4(Te6edf3raw_nameTb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3name Tff7b72or Te6edf3name Tff7b72not Tff7b72in Te6edf3interfacesTb4b4b4:
Te6edf3missingTff7b72.Td2a8ffappendTb4b4b4(Tffa657strTb4b4b4(Te6edf3raw_nameTb4b4b4)Tb4b4b4)
Tff7b72continue
Te6edf3details Tff7b72= Te6edf3interfacesTb4b4b4[Te6edf3nameTb4b4b4]
Tff7b72if Te6edf3raw_bps Tff7b72is Tff7b72None Tff7b72or Te6edf3raw_bps Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3detailsTff7b72.Td2a8ffpopTb4b4b4(Ta5d6ff"Ta5d6ffbitrateTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3bps Tff7b72= Tffa657intTb4b4b4(Te6edf3raw_bpsTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffInvalid bitrate for Tffd700{Te6edf3nameTffd700}Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3bps Tff7b72< T79c0ff0Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffBitrate must be >= 0 for Tffd700{Te6edf3nameTffd700}Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3detailsTb4b4b4[Ta5d6ff"Ta5d6ffbitrateTa5d6ff"Tb4b4b4] Tff7b72= Tffa657strTb4b4b4(Te6edf3bpsTb4b4b4)
Te6edf3updatedTff7b72.Td2a8ffappendTb4b4b4(Te6edf3nameTb4b4b4)
Tff7b72if Tff7b72not Te6edf3updated Tff7b72and Te6edf3missingTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffNo matching interfacesTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffmissingTa5d6ff"Tb4b4b4: Te6edf3missingTb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff404Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3updated Tff7b72and Tff7b72not Te6edf3appTff7b72.Td2a8ff_write_reticulum_configTb4b4b4(
Te6edf3rollback_interfacesTff7b72=Te6edf3interfaces_before_writeTb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFailed to write Reticulum configTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Te6edf3reloaded Tff7b72= Tff7b72False
Tff7b72if Te6edf3reload_stack Tff7b72and Te6edf3updatedTb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72await Te6edf3appTff7b72.Td2a8ffreload_reticulumTb4b4b4(Tb4b4b4)
Te6edf3reloaded Tff7b72= Tff7b72True
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffBitrates saved but RNS reload failed: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffupdatedTa5d6ff"Tb4b4b4: Te6edf3updatedTb4b4b4,
Ta5d6ff"Ta5d6ffmissingTa5d6ff"Tb4b4b4: Te6edf3missingTb4b4b4,
Ta5d6ff"Ta5d6ffreloadedTa5d6ff"Tb4b4b4: Tff7b72FalseTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface bitrates updatedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffupdatedTa5d6ff"Tb4b4b4: Te6edf3updatedTb4b4b4,
Ta5d6ff"Ta5d6ffmissingTa5d6ff"Tb4b4b4: Te6edf3missingTb4b4b4,
Ta5d6ff"Ta5d6ffreloadedTa5d6ff"Tb4b4b4: Te6edf3reloadedTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e# fetch community interfaces
T8b949e# enable reticulum interface
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/enableTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interfaces_enableTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get request data
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3interface_name Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_name Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_name Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface name is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# enable interface
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_sectionTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3interface_name Tff7b72not Tff7b72in Te6edf3interfacesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface not foundTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff404Tb4b4b4,
Tb4b4b4)
Te6edf3interface Tff7b72= Te6edf3interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4]
Te6edf3i2p_error Tff7b72= Te6edf3i2p_supportTff7b72.Td2a8ffvalidate_i2p_enableTb4b4b4(
Te6edf3interfacesTb4b4b4,
Te6edf3appTff7b72.Td2a8ff_get_reticulum_sectionTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3interface_nameTff7b72=Te6edf3interface_nameTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3i2p_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3i2p_errorTb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff422Tb4b4b4)
Tff7b72if Ta5d6ff"Ta5d6ffenabledTa5d6ff" Tff7b72in Te6edf3interfaceTb4b4b4:
Te6edf3interfaceTb4b4b4[Ta5d6ff"Ta5d6ffenabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fftrueTa5d6ff"
Tff7b72if Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff" Tff7b72in Te6edf3interfaceTb4b4b4:
Te6edf3interfaceTb4b4b4[Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fftrueTa5d6ff"
Te6edf3keys_to_remove Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3keyTb4b4b4, Te6edf3value Tff7b72in Te6edf3interfaceTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3value Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3keys_to_removeTff7b72.Td2a8ffappendTb4b4b4(Te6edf3keyTb4b4b4)
Tff7b72for Te6edf3key Tff7b72in Te6edf3keys_to_removeTb4b4b4:
Tff7b72del Te6edf3interfaceTb4b4b4[Te6edf3keyTb4b4b4]
T8b949e# save config
Tff7b72if Tff7b72not Te6edf3appTff7b72.Td2a8ff_write_reticulum_configTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFailed to write Reticulum configTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface is now enabledTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e# disable reticulum interface
T8b949e# disable reticulum interface
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/disableTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interfaces_disableTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get request data
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3interface_name Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_name Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_name Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface name is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# disable interface
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_sectionTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3interface_name Tff7b72not Tff7b72in Te6edf3interfacesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface not foundTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff404Tb4b4b4,
Tb4b4b4)
Te6edf3interface Tff7b72= Te6edf3interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4]
Tff7b72if Ta5d6ff"Ta5d6ffenabledTa5d6ff" Tff7b72in Te6edf3interfaceTb4b4b4:
Te6edf3interfaceTb4b4b4[Ta5d6ff"Ta5d6ffenabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fffalseTa5d6ff"
Tff7b72if Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff" Tff7b72in Te6edf3interfaceTb4b4b4:
Te6edf3interfaceTb4b4b4[Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fffalseTa5d6ff"
Te6edf3keys_to_remove Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3keyTb4b4b4, Te6edf3value Tff7b72in Te6edf3interfaceTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3value Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3keys_to_removeTff7b72.Td2a8ffappendTb4b4b4(Te6edf3keyTb4b4b4)
Tff7b72for Te6edf3key Tff7b72in Te6edf3keys_to_removeTb4b4b4:
Tff7b72del Te6edf3interfaceTb4b4b4[Te6edf3keyTb4b4b4]
T8b949e# save config
Tff7b72if Tff7b72not Te6edf3appTff7b72.Td2a8ff_write_reticulum_configTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFailed to write Reticulum configTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface deletedTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e# delete reticulum interface
T8b949e# delete reticulum interface
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/deleteTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interfaces_deleteTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get request data
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3interface_name Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_name Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_name Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface name is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_sectionTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3interface_name Tff7b72not Tff7b72in Te6edf3interfacesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface not foundTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff404Tb4b4b4,
Tb4b4b4)
T8b949e# delete interface
Tff7b72del Te6edf3interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4]
T8b949e# save config
Tff7b72if Tff7b72not Te6edf3appTff7b72.Td2a8ff_write_reticulum_configTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFailed to write Reticulum configTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface has been deletedTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interface-modulesTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interface_modules_listTb4b4b4(Te6edf3_requestTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787interface_module_store Tff7b72import Tb4b4b4(
Te6edf3list_interface_modulesTb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3payload Tff7b72= Te6edf3list_interface_modulesTb4b4b4(Te6edf3appTff7b72.Td2a8ffreticulum_config_dirTb4b4b4)
Tff7b72except Tf85149ValueError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to list interface modules: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Te6edf3payloadTb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interface-modulesTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interface_modules_installTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787interface_module_store Tff7b72import Tb4b4b4(
Te6edf3install_interface_moduleTb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3content_type Tff7b72= Te6edf3requestTff7b72.Td2a8ffheadersTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffContent-TypeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3filename Tff7b72= Tff7b72None
Te6edf3data Tff7b72= Ta5d6ffbTa5d6ff"Ta5d6ff"
Te6edf3overwrite Tff7b72= Tff7b72False
Tff7b72if Ta5d6ff"Ta5d6ffmultipart/form-dataTa5d6ff" Tff7b72in Te6edf3content_typeTb4b4b4:
Te6edf3reader Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffmultipartTb4b4b4(Tb4b4b4)
Te6edf3field Tff7b72= Tff7b72await Te6edf3readerTff7b72.Td2a8ffnextTb4b4b4(Tb4b4b4)
Tff7b72while Te6edf3field Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3fieldTff7b72.Td2a8ffname Tff7b72== Ta5d6ff"Ta5d6fffileTa5d6ff"Tb4b4b4:
Te6edf3filename Tff7b72= Te6edf3fieldTff7b72.Td2a8fffilename Tff7b72or Te6edf3filename
Te6edf3chunks Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3chunk Tff7b72= Tff7b72await Te6edf3fieldTff7b72.Td2a8ffread_chunkTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3chunkTb4b4b4:
Tff7b72break
Te6edf3chunksTff7b72.Td2a8ffappendTb4b4b4(Te6edf3chunkTb4b4b4)
Te6edf3data Tff7b72= Ta5d6ffbTa5d6ff"Ta5d6ff"Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3chunksTb4b4b4)
Tff7b72elif Te6edf3fieldTff7b72.Td2a8ffname Tff7b72== Ta5d6ff"Ta5d6ffoverwriteTa5d6ff"Tb4b4b4:
Te6edf3overwrite Tff7b72= Tb4b4b4(Tff7b72await Te6edf3fieldTff7b72.Td2a8fftextTb4b4b4(Tb4b4b4)Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72in Tb4b4b4(
Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fftrueTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffyesTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffonTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72elif Te6edf3fieldTff7b72.Td2a8ffname Tff7b72== Ta5d6ff"Ta5d6fffilenameTa5d6ff"Tb4b4b4:
Te6edf3filename Tff7b72= Tb4b4b4(Tff7b72await Te6edf3fieldTff7b72.Td2a8fftextTb4b4b4(Tb4b4b4)Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72or Te6edf3filename
Te6edf3field Tff7b72= Tff7b72await Te6edf3readerTff7b72.Td2a8ffnextTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3body Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3filename Tff7b72= Te6edf3bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffilenameTa5d6ff"Tb4b4b4) Tff7b72or Te6edf3bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4)
Te6edf3raw Tff7b72= Te6edf3bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4) Tff7b72or Te6edf3bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdataTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3rawTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3data Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3rawTb4b4b4, Te6edf3validateTff7b72=Tff7b72FalseTb4b4b4)
Tff7b72except Tb4b4b4(Te6edf3binasciiTff7b72.Td2a8ffErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Te6edf3rawTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Tff7b72elif Tffa657isinstanceTb4b4b4(Te6edf3rawTb4b4b4, Tb4b4b4(Tffa657bytesTb4b4b4, Tffa657bytearrayTb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tffa657bytesTb4b4b4(Te6edf3rawTb4b4b4)
Te6edf3overwrite Tff7b72= Tffa657boolTb4b4b4(Te6edf3bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffoverwriteTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3dataTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface module file is requiredTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Te6edf3result Tff7b72= Te6edf3install_interface_moduleTb4b4b4(
Te6edf3appTff7b72.Td2a8ffreticulum_config_dirTb4b4b4,
Te6edf3filenameTff7b72=Te6edf3filenameTb4b4b4,
Te6edf3dataTff7b72=Te6edf3dataTb4b4b4,
Te6edf3overwriteTff7b72=Te6edf3overwriteTb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffInstalled Tffd700{Te6edf3resultTb4b4b4[Ta5d6ff'Ta5d6fffilenameTa5d6ff'Tb4b4b4]Tffd700}Ta5d6ff. Ta5d6ff"
Ta5d6ff"Ta5d6ffReload Reticulum or restart MeshChatX to load it.Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tff7b72*Tff7b72*Te6edf3resultTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149ValueError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff422Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to install interface module: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tf0883e@routesTff7b72.Td2a8ffdeleteTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interface-modules/Tffd700{type_name}Ta5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interface_modules_deleteTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787interface_module_store Tff7b72import Tb4b4b4(
Te6edf3delete_interface_moduleTb4b4b4,
Tb4b4b4)
Te6edf3type_name Tff7b72= Te6edf3requestTff7b72.Td2a8ffmatch_infoTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftype_nameTa5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3result Tff7b72= Te6edf3delete_interface_moduleTb4b4b4(Te6edf3appTff7b72.Td2a8ffreticulum_config_dirTb4b4b4, Te6edf3type_nameTb4b4b4)
Tff7b72except Tf85149FileNotFoundError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff404Tb4b4b4)
Tff7b72except Tf85149ValueError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff422Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to delete interface module: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffDeleted Tffd700{Te6edf3resultTb4b4b4[Ta5d6ff'Ta5d6fffilenameTa5d6ff'Tb4b4b4]Tffd700}Ta5d6ff"Tb4b4b4,
Tff7b72*Tff7b72*Te6edf3resultTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e# add reticulum interface
T8b949e# add reticulum interface
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/addTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffreticulum_interfaces_addTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# get request data
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3interface_name Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffsanitize_interface_section_nameTb4b4b4(
Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3interface_type Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4)
Te6edf3allow_overwriting_interface Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffallow_overwriting_interfaceTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4)
T8b949e# ensure name is provided
Tff7b72if Te6edf3interface_name Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_name Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffName is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure type name provided
Tff7b72if Te6edf3interface_type Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffType is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# get existing interfaces
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_sectionTb4b4b4(Tb4b4b4)
T8b949e# ensure name is not for an existing interface, to prevent overwriting
Tff7b72if Te6edf3allow_overwriting_interface Tff7b72is Tff7b72False Tff7b72and Te6edf3interface_name Tff7b72in Te6edf3interfacesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffName is already in use by another interfaceTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3i2p_error Tff7b72= Te6edf3i2p_supportTff7b72.Td2a8ffvalidate_i2p_add_or_updateTb4b4b4(
Te6edf3interfacesTb4b4b4,
Te6edf3appTff7b72.Td2a8ff_get_reticulum_sectionTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3interface_nameTff7b72=Te6edf3interface_nameTb4b4b4,
Te6edf3interface_typeTff7b72=Te6edf3interface_typeTb4b4b4,
Te6edf3updating_existingTff7b72=Tffa657boolTb4b4b4(Te6edf3allow_overwriting_interfaceTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3i2p_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3i2p_errorTb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff422Tb4b4b4)
T8b949e# get existing interface details if available
Te6edf3interface_details Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72if Te6edf3interface_name Tff7b72in Te6edf3interfacesTb4b4b4:
Te6edf3interface_details Tff7b72= Te6edf3interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4]
T8b949e# update interface details
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_type
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4:
T8b949e# RNS has no Android-specific implementation of RNodeMultiInterface,
T8b949e# so it always crashes on Android regardless of transport.
Tff7b72if Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6ff"Ta5d6ffRNodeMultiInterface is not supported on Android Ta5d6ff"
Ta5d6ff"Ta5d6ff(Reticulum has no Android-specific implementation of it).Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72elif Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffRNodeInterfaceTa5d6ff"Tb4b4b4:
T8b949e# RNodeIPInterface always maps to an RNodeInterface with a tcp://
T8b949e# port, which needs no native Android modules and always works.
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787rnode_support Tff7b72import Tb4b4b4(
Te6edf3rnode_transport_supportedTb4b4b4,
Tb4b4b4)
Te6edf3probe_interface Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4: Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffallow_bluetoothTa5d6ff"Tb4b4b4: Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffallow_bluetoothTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72if Tff7b72not Te6edf3rnode_transport_supportedTb4b4b4(
Te6edf3probe_interfaceTb4b4b4,
Te6edf3is_androidTff7b72=Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3_is_chaquopy_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3message Tff7b72= Tb4b4b4(
Ta5d6ff"Ta5d6ffThis RNode connection type is not available on this device. Ta5d6ff"
Ta5d6ff"Ta5d6ffOn Android, USB serial and classic Bluetooth need the bundled Ta5d6ff"
Ta5d6ff"Ta5d6ffUSB host stack, and BLE needs the bundled able stack. Ta5d6ff"
Ta5d6ff"Ta5d6ffRNode over IP (TCP) is unaffected.Ta5d6ff"
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3message Tff7b72= Tb4b4b4(
Ta5d6ff"Ta5d6ffThis RNode connection type is not available on this device. Ta5d6ff"
Ta5d6ff"Ta5d6ffUSB serial and classic Bluetooth need pyserial; BLE needs bleak. Ta5d6ff"
Ta5d6ff"Ta5d6ffRNode over IP (TCP) is unaffected.Ta5d6ff"
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3messageTb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# if interface doesn't have enabled or interface_enabled setting already, enable it by default
Tff7b72if Tb4b4b4(
Ta5d6ff"Ta5d6ffenabledTa5d6ff" Tff7b72not Tff7b72in Te6edf3interface_details
Tff7b72and Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff" Tff7b72not Tff7b72in Te6edf3interface_details
Tb4b4b4)Tb4b4b4:
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fftrueTa5d6ff"
T8b949e# handle AutoInterface
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffAutoInterfaceTa5d6ff"Tb4b4b4:
T8b949e# validate scope value if provided
Te6edf3discovery_scope_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdiscovery_scopeTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3discovery_scope_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72if Tffa657strTb4b4b4(Te6edf3discovery_scope_valueTb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72not Tff7b72in Tb4b4b4{
Ta5d6ff"Ta5d6fflinkTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffadminTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffsiteTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fforganisationTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffglobalTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6ff"Ta5d6ffDiscovery scope must be one of: link, admin, Ta5d6ff"
Ta5d6ff"Ta5d6ffsite, organisation, globalTa5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3multicast_address_type_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmulticast_address_typeTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3multicast_address_type_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4) Tff7b72and Tffa657strTb4b4b4(
Te6edf3multicast_address_type_valueTb4b4b4,
Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72not Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6fftemporaryTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffpermanentTa5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6ff"Ta5d6ffMulticast address type must be either Ta5d6ff'Ta5d6fftemporaryTa5d6ff'Ta5d6ff or Ta5d6ff'Ta5d6ffpermanentTa5d6ff'Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# validate ports if provided and ensure they are not in use
Te6edf3discovery_port_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdiscovery_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3discovery_port_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4) Tff7b72and Te6edf3is_port_in_useTb4b4b4(
Tff7b72NoneTb4b4b4,
Te6edf3discovery_port_valueTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6ffudpTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3describe_port_conflictTb4b4b4(
Tff7b72NoneTb4b4b4,
Te6edf3discovery_port_valueTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6ffudpTa5d6ff"Tb4b4b4,
Te6edf3interface_nameTff7b72=Te6edf3interface_nameTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff409Tb4b4b4,
Tb4b4b4)
Te6edf3data_port_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdata_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3data_port_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4) Tff7b72and Te6edf3is_port_in_useTb4b4b4(
Tff7b72NoneTb4b4b4,
Te6edf3data_port_valueTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6ffudpTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3describe_port_conflictTb4b4b4(
Tff7b72NoneTb4b4b4,
Te6edf3data_port_valueTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6ffudpTa5d6ff"Tb4b4b4,
Te6edf3interface_nameTff7b72=Te6edf3interface_nameTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff409Tb4b4b4,
Tb4b4b4)
T8b949e# set optional AutoInterface options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffgroup_idTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffmulticast_address_typeTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdevicesTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffignored_devicesTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdiscovery_scopeTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdiscovery_portTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdata_portTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffconfigured_bitrateTa5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e# handle TCPClientInterface
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffTCPClientInterfaceTa5d6ff"Tb4b4b4:
T8b949e# ensure target host provided
Te6edf3interface_target_host Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftarget_hostTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_target_host Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_target_host Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffTarget Host is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure target port provided
Te6edf3interface_target_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftarget_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_target_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_target_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffTarget Port is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# set required TCPClientInterface options
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fftarget_hostTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_target_host
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fftarget_portTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_target_port
T8b949e# set optional TCPClientInterface options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffkiss_framingTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffi2p_tunneledTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffconnect_timeoutTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffmax_reconnect_triesTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3fixed_mtu_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffapply_fixed_mtuTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3fixed_mtu_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3fixed_mtu_errorTb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffBackboneInterfaceTa5d6ff"Tb4b4b4:
T8b949e# BackboneInterface supports two distinct configurations:
T8b949e# - listener mode: bind to listen_ip/listen_port to accept peers
T8b949e# - connector mode: dial out to remote/target_port for a relay
Te6edf3listen_port_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4)
Te6edf3listen_ip_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_ipTa5d6ff"Tb4b4b4)
Te6edf3listen_device_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffdeviceTa5d6ff"Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3listen_port_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4) Tff7b72and Tb4b4b4(
Te6edf3listen_ip_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72or Te6edf3listen_device_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3is_port_in_useTb4b4b4(
Te6edf3listen_ip_valueTb4b4b4,
Te6edf3listen_port_valueTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6fftcpTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3describe_port_conflictTb4b4b4(
Te6edf3listen_ip_valueTb4b4b4,
Te6edf3listen_port_valueTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6fftcpTa5d6ff"Tb4b4b4,
Te6edf3interface_nameTff7b72=Te6edf3interface_nameTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff409Tb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3listen_port_value
Tff7b72if Te6edf3listen_ip_value Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_ipTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3listen_ip_value
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdeviceTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffprefer_ipv6Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3flap_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffapply_backbone_fast_flappingTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3flap_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3flap_errorTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3remote Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffremoteTa5d6ff"Tb4b4b4) Tff7b72or Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftarget_hostTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3remote Tff7b72is Tff7b72None Tff7b72or Tffa657strTb4b4b4(Te6edf3remoteTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffRemote host is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_target_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftarget_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_target_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_target_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffTarget Port is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffremoteTa5d6ff"Tb4b4b4] Tff7b72= Tffa657strTb4b4b4(Te6edf3remoteTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fftarget_portTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_target_port
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6fftransport_identityTa5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e# handle I2P interface
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffI2PInterfaceTa5d6ff"Tb4b4b4:
Te6edf3connectable_value Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffconnectableTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3connectable_value Tff7b72is Tff7b72None Tff7b72or Te6edf3connectable_value Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffconnectableTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ffTrueTa5d6ff"
Tff7b72elseTb4b4b4:
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffconnectableTa5d6ff"Tb4b4b4] Tff7b72= Tb4b4b4(
Ta5d6ff"Ta5d6ffTrueTa5d6ff"
Tff7b72if Tffa657strTb4b4b4(Te6edf3connectable_valueTb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6fftrueTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffyesTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffonTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffyTa5d6ff"Tb4b4b4}
Tff7b72else Ta5d6ff"Ta5d6ffFalseTa5d6ff"
Tb4b4b4)
Te6edf3peers Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpeersTa5d6ff"Tb4b4b4)
Te6edf3cleaned_peersTb4b4b4: Tffa657listTb4b4b4[Tffa657strTb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3peersTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4:
Te6edf3cleaned_peers Tff7b72= Tb4b4b4[Tffa657strTb4b4b4(Te6edf3pTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72for Te6edf3p Tff7b72in Te6edf3peers Tff7b72if Tffa657strTb4b4b4(Te6edf3pTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4]
Tff7b72elif Te6edf3peers Tff7b72is Tff7b72not Tff7b72None Tff7b72and Tffa657strTb4b4b4(Te6edf3peersTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3cleaned_peers Tff7b72= Tb4b4b4[
Te6edf3sTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72for Te6edf3s Tff7b72in Tffa657strTb4b4b4(Te6edf3peersTb4b4b4)Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Ta5d6ff,Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffsplitTb4b4b4(Tb4b4b4) Tff7b72if Te6edf3sTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tb4b4b4]
Tff7b72if Tff7b72not Te6edf3cleaned_peersTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffAt least one I2P peer is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffpeersTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3cleaned_peers
T8b949e# handle tcp server interface
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffTCPServerInterfaceTa5d6ff"Tb4b4b4:
T8b949e# ensure listen ip provided
Te6edf3interface_listen_ip Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_ipTa5d6ff"Tb4b4b4)
Tff7b72if Tb4b4b4(
Te6edf3interface_listen_ip Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Tffa657strTb4b4b4(Te6edf3interface_listen_ipTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72!= Ta5d6ff"Ta5d6ff"
Tb4b4b4)Tb4b4b4:
Te6edf3interface_listen_ip Tff7b72= Tffa657strTb4b4b4(Te6edf3interface_listen_ipTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3interface_listen_ip Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3interface_listen_ip Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffListen IP is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure listen port provided
Te6edf3interface_listen_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_listen_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_listen_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffListen Port is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure listen port is not currently in use by another process
Tff7b72if Te6edf3is_port_in_useTb4b4b4(
Te6edf3interface_listen_ipTb4b4b4,
Te6edf3interface_listen_portTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6fftcpTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3describe_port_conflictTb4b4b4(
Te6edf3interface_listen_ipTb4b4b4,
Te6edf3interface_listen_portTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6fftcpTa5d6ff"Tb4b4b4,
Te6edf3interface_nameTff7b72=Te6edf3interface_nameTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff409Tb4b4b4,
Tb4b4b4)
T8b949e# set required TCPServerInterface options
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_ipTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_listen_ip
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_listen_port
T8b949e# set optional TCPServerInterface options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdeviceTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffprefer_ipv6Ta5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffi2p_tunneledTa5d6ff"Tb4b4b4)
T8b949e# handle udp interface
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffUDPInterfaceTa5d6ff"Tb4b4b4:
T8b949e# ensure listen ip provided
Te6edf3interface_listen_ip Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_ipTa5d6ff"Tb4b4b4)
Tff7b72if Tb4b4b4(
Te6edf3interface_listen_ip Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Tffa657strTb4b4b4(Te6edf3interface_listen_ipTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72!= Ta5d6ff"Ta5d6ff"
Tb4b4b4)Tb4b4b4:
Te6edf3interface_listen_ip Tff7b72= Tffa657strTb4b4b4(Te6edf3interface_listen_ipTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3interface_listen_ip Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3interface_listen_ip Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffListen IP is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure listen port provided
Te6edf3interface_listen_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_listen_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_listen_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffListen Port is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure forward ip provided
Te6edf3interface_forward_ip Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffforward_ipTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_forward_ip Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_forward_ip Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffForward IP is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure forward port provided
Te6edf3interface_forward_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffforward_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_forward_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_forward_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffForward Port is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure listen port is not currently in use by another process
Tff7b72if Te6edf3is_port_in_useTb4b4b4(
Te6edf3interface_listen_ipTb4b4b4,
Te6edf3interface_listen_portTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6ffudpTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3describe_port_conflictTb4b4b4(
Te6edf3interface_listen_ipTb4b4b4,
Te6edf3interface_listen_portTb4b4b4,
Te6edf3kindTff7b72=Ta5d6ff"Ta5d6ffudpTa5d6ff"Tb4b4b4,
Te6edf3interface_nameTff7b72=Te6edf3interface_nameTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff409Tb4b4b4,
Tb4b4b4)
T8b949e# set required UDPInterface options
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_ipTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_listen_ip
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_listen_port
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffforward_ipTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_forward_ip
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffforward_portTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_forward_port
T8b949e# set optional UDPInterface options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdeviceTa5d6ff"Tb4b4b4)
T8b949e# handle RNodeInterface and RNodeIPInterface
Tff7b72if Te6edf3interface_type Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffRNodeInterfaceTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffRNodeIPInterfaceTa5d6ff"Tb4b4b4)Tb4b4b4:
T8b949e# map RNodeIPInterface to RNodeInterface for Reticulum config
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ffRNodeInterfaceTa5d6ff"
T8b949e# ensure port provided
Te6edf3interface_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffPort is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_tcp_host Tff7b72= Tff7b72None
Tff7b72if Tffa657strTb4b4b4(Te6edf3interface_portTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6fftcp://Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3interface_port Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffnormalize_rnode_tcp_portTb4b4b4(
Tffa657strTb4b4b4(Te6edf3interface_portTb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3host_part Tff7b72= Tffa657strTb4b4b4(Te6edf3interface_portTb4b4b4)Tb4b4b4[Tffa657lenTb4b4b4(Ta5d6ff"Ta5d6fftcp://Ta5d6ff"Tb4b4b4) Tb4b4b4:Tb4b4b4]Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Ta5d6ff"Ta5d6ff:Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3host_partTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffTCP host is required for RNode over IPTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_tcp_host Tff7b72= Te6edf3host_part
T8b949e# ensure frequency provided
Te6edf3interface_frequency Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_frequency Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_frequency Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffFrequency is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure bandwidth provided
Te6edf3interface_bandwidth Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffbandwidthTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_bandwidth Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_bandwidth Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffBandwidth is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure txpower provided and within Reticulum limits
Te6edf3interface_txpower Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4)
Te6edf3txpower_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffvalidate_rnode_txpowerTb4b4b4(
Te6edf3interface_txpowerTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3txpower_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3txpower_errorTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure spreading factor provided
Te6edf3interface_spreadingfactor Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffspreadingfactorTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_spreadingfactor Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_spreadingfactor Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffSpreading Factor is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure coding rate provided
Te6edf3interface_codingrate Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcodingrateTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_codingrate Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_codingrate Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffCoding Rate is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# set required RNodeInterface options
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_port
Tff7b72if Te6edf3interface_tcp_host Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
T8b949e# RNS's Android-specific RNodeInterface reads tcp_host as its
T8b949e# own config key instead of parsing it out of port like the
T8b949e# desktop implementation does, so both must be set for RNode
T8b949e# over IP to work on Android.
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fftcp_hostTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_tcp_host
Tff7b72elseTb4b4b4:
Te6edf3interface_detailsTff7b72.Td2a8ffpopTb4b4b4(Ta5d6ff"Ta5d6fftcp_hostTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffcoerce_rnode_frequency_hzTb4b4b4(
Te6edf3interface_frequencyTb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffbandwidthTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_bandwidth
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffnormalize_rnode_txpowerTb4b4b4(
Te6edf3interface_txpowerTb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffspreadingfactorTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_spreadingfactor
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffcodingrateTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_codingrate
T8b949e# set optional RNodeInterface options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffcallsignTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffid_callsignTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffid_intervalTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffflow_controlTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffairtime_limit_longTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffairtime_limit_shortTa5d6ff"Tb4b4b4,
Tb4b4b4)
T8b949e# handle RNodeMultiInterface
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4:
T8b949e# required settings
Te6edf3interface_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4)
Te6edf3sub_interfaces Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffsub_interfacesTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)
T8b949e# ensure port provided
Tff7b72if Te6edf3interface_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffPort is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure sub interfaces provided
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3sub_interfacesTb4b4b4, Tffa657listTb4b4b4) Tff7b72or Tff7b72not Te6edf3sub_interfacesTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffAt least one sub-interface is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# set required RNodeMultiInterface options
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_port
T8b949e# remove any existing sub interfaces, which can be found by finding keys that contain a dict value
T8b949e# this allows us to replace all sub interfaces with the ones we are about to add, while also ensuring
T8b949e# that we do not remove any existing config values from the main interface config
Tff7b72for Te6edf3key Tff7b72in Tffa657listTb4b4b4(Te6edf3interface_detailsTff7b72.Td2a8ffkeysTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3value Tff7b72= Te6edf3interface_detailsTb4b4b4[Te6edf3keyTb4b4b4]
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3valueTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72del Te6edf3interface_detailsTb4b4b4[Te6edf3keyTb4b4b4]
T8b949e# process each provided sub interface
Te6edf3required_subinterface_fields Tff7b72= Tb4b4b4[
Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffbandwidthTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffspreadingfactorTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffcodingrateTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffvportTa5d6ff"Tb4b4b4,
Tb4b4b4]
Tff7b72for Te6edf3idxTb4b4b4, Te6edf3sub_interface Tff7b72in Tffa657enumerateTb4b4b4(Te6edf3sub_interfacesTb4b4b4)Tb4b4b4:
T8b949e# ensure required fields for sub-interface provided
Te6edf3missing_fields Tff7b72= Tb4b4b4[
Te6edf3field
Tff7b72for Te6edf3field Tff7b72in Te6edf3required_subinterface_fields
Tff7b72if Tb4b4b4(
Te6edf3field Tff7b72not Tff7b72in Te6edf3sub_interface
Tff7b72or Te6edf3sub_interfaceTff7b72.Td2a8ffgetTb4b4b4(Te6edf3fieldTb4b4b4) Tff7b72is Tff7b72None
Tff7b72or Te6edf3sub_interfaceTff7b72.Td2a8ffgetTb4b4b4(Te6edf3fieldTb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ff"
Tb4b4b4)
Tb4b4b4]
Tff7b72if Te6edf3missing_fieldsTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffSub-interface Tffd700{Te6edf3idx Tff7b72+ T79c0ff1Tffd700}Ta5d6ff is missing required field(s): Tffd700{Ta5d6ff'Ta5d6ff, Ta5d6ff'Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3missing_fieldsTb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3sub_txpower_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffvalidate_rnode_txpowerTb4b4b4(
Te6edf3sub_interfaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3sub_txpower_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffSub-interface Tffd700{Te6edf3idx Tff7b72+ T79c0ff1Tffd700}Ta5d6ff: Tffd700{Te6edf3sub_txpower_errorTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3sub_interface_name Tff7b72= Te6edf3sub_interfaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Te6edf3sub_interface_nameTb4b4b4] Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fftrueTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4: Te6edf3InterfaceEditorTff7b72.Td2a8ffcoerce_rnode_frequency_hzTb4b4b4(
Te6edf3sub_interfaceTb4b4b4[Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffbandwidthTa5d6ff"Tb4b4b4: Tffa657intTb4b4b4(Te6edf3sub_interfaceTb4b4b4[Ta5d6ff"Ta5d6ffbandwidthTa5d6ff"Tb4b4b4]Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4: Te6edf3InterfaceEditorTff7b72.Td2a8ffnormalize_rnode_txpowerTb4b4b4(
Te6edf3sub_interfaceTb4b4b4[Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffspreadingfactorTa5d6ff"Tb4b4b4: Tffa657intTb4b4b4(Te6edf3sub_interfaceTb4b4b4[Ta5d6ff"Ta5d6ffspreadingfactorTa5d6ff"Tb4b4b4]Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcodingrateTa5d6ff"Tb4b4b4: Tffa657intTb4b4b4(Te6edf3sub_interfaceTb4b4b4[Ta5d6ff"Ta5d6ffcodingrateTa5d6ff"Tb4b4b4]Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffvportTa5d6ff"Tb4b4b4: Tffa657intTb4b4b4(Te6edf3sub_interfaceTb4b4b4[Ta5d6ff"Ta5d6ffvportTa5d6ff"Tb4b4b4]Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Te6edf3interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4] Tff7b72= Te6edf3interface_details
T8b949e# handle SerialInterface, KISSInterface, and AX25KISSInterface
Tff7b72if Te6edf3interface_type Tff7b72in Tb4b4b4(
Ta5d6ff"Ta5d6ffSerialInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffKISSInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffAX25KISSInterfaceTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
T8b949e# ensure port provided
Te6edf3interface_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_port Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffPort is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# set required options
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_port
T8b949e# set optional options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffspeedTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffdatabitsTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffparityTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffstopbitsTa5d6ff"Tb4b4b4)
T8b949e# Handle KISS and AX25KISS specific options
Tff7b72if Te6edf3interface_type Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffKISSInterfaceTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffAX25KISSInterfaceTa5d6ff"Tb4b4b4)Tb4b4b4:
T8b949e# set optional options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffpreambleTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6fftxtailTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffpersistenceTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffslottimeTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffflow_controlTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffid_callsignTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffid_intervalTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffcallsignTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffssidTa5d6ff"Tb4b4b4)
T8b949e# RNode Airtime limits and station ID
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffcallsignTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffid_intervalTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffairtime_limit_longTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffairtime_limit_shortTa5d6ff"Tb4b4b4)
T8b949e# handle Pipe Interface
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffPipeInterfaceTa5d6ff"Tb4b4b4:
T8b949e# ensure command provided
Te6edf3interface_command Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcommandTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_command Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_command Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffCommand is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# ensure command provided
Te6edf3interface_respawn_delay Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffrespawn_delayTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_respawn_delay Tff7b72is Tff7b72None Tff7b72or Te6edf3interface_respawn_delay Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffRespawn delay is requiredTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
T8b949e# set required options
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffcommandTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_command
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffrespawn_delayTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3interface_respawn_delay
T8b949e# HTTP tunnel (vendored RNS-over-HTTP). Config mode is client|server,
T8b949e# which is distinct from Reticulum interface modes (full/gateway/...).
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffHTTPInterfaceTa5d6ff"Tb4b4b4:
Te6edf3tunnel_mode Tff7b72= Tffa657strTb4b4b4(Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmodeTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3tunnel_mode Tff7b72not Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6ffclientTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffserverTa5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffHTTPInterface mode must be client or serverTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffmodeTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3tunnel_mode
Te6edf3http_version_raw Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffhttp_versionTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3http_version_raw Tff7b72not Tff7b72in Tb4b4b4(Tff7b72NoneTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3http_version Tff7b72= Tffa657intTb4b4b4(Te6edf3http_version_rawTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffhttp_version must be 1, 2, or 3Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3http_version Tff7b72not Tff7b72in Tb4b4b4(T79c0ff1Tb4b4b4, T79c0ff2Tb4b4b4, T79c0ff3Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffhttp_version must be 1, 2, or 3Ta5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffhttp_versionTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3http_version
Tff7b72elseTb4b4b4:
Te6edf3interface_detailsTff7b72.Td2a8ffpopTb4b4b4(Ta5d6ff"Ta5d6ffhttp_versionTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72if Te6edf3tunnel_mode Tff7b72== Ta5d6ff"Ta5d6ffclientTa5d6ff"Tb4b4b4:
Te6edf3server_url Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffserver_urlTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3server_url Tff7b72is Tff7b72None Tff7b72or Tffa657strTb4b4b4(Te6edf3server_urlTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffserver_url is required for HTTPInterface client modeTa5d6ff"
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6ffserver_urlTa5d6ff"Tb4b4b4] Tff7b72= Tffa657strTb4b4b4(Te6edf3server_urlTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffpoll_intervalTa5d6ff"Tb4b4b4)
Tff7b72for Te6edf3key Tff7b72in Tb4b4b4(
Ta5d6ff"Ta5d6fflisten_hostTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffcheck_user_agentTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffserve_html_pageTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffhtml_file_pathTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fftls_certfileTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fftls_keyfileTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Te6edf3interface_detailsTff7b72.Td2a8ffpopTb4b4b4(Te6edf3keyTb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3listen_host Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_hostTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3listen_host Tff7b72is Tff7b72None Tff7b72or Tffa657strTb4b4b4(Te6edf3listen_hostTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3listen_host Tff7b72= Ta5d6ff"Ta5d6ff0.0.0.0Ta5d6ff"
Te6edf3listen_port Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3listen_port Tff7b72is Tff7b72None Tff7b72or Te6edf3listen_port Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fflisten_port is required for HTTPInterface server modeTa5d6ff"
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_hostTa5d6ff"Tb4b4b4] Tff7b72= Tffa657strTb4b4b4(Te6edf3listen_hostTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3interface_detailsTb4b4b4[Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3listen_port
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(
Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffcheck_user_agentTa5d6ff"
Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffserve_html_pageTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffhtml_file_pathTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6fftls_certfileTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6fftls_keyfileTa5d6ff"Tb4b4b4)
Tff7b72for Te6edf3key Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffserver_urlTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffpoll_intervalTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3interface_detailsTff7b72.Td2a8ffpopTb4b4b4(Te6edf3keyTb4b4b4, Tff7b72NoneTb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffmtuTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffuser_agentTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffpool_connectionsTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffpool_maxsizeTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffkeepalive_timeoutTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6fftls_verifyTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6fftls_ca_certsTa5d6ff"Tb4b4b4)
Te6edf3_builtin_interface_types Tff7b72= Tffa657frozensetTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffAutoInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffTCPClientInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffBackboneInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffI2PInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffTCPServerInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffUDPInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffRNodeInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffRNodeIPInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffSerialInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffKISSInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffAX25KISSInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffPipeInterfaceTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffHTTPInterfaceTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3interface_type Tff7b72not Tff7b72in Te6edf3_builtin_interface_typesTb4b4b4:
Te6edf3extra Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffextra_configTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3extra Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3extra Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3extraTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffextra_config must be a JSON objectTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72for Te6edf3keyTb4b4b4, Te6edf3value Tff7b72in Te6edf3extraTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3key Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffallow_overwriting_interfaceTa5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72continue
Tff7b72if Te6edf3value Tff7b72is Tff7b72None Tff7b72or Te6edf3value Tff7b72== Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3interface_detailsTff7b72.Td2a8ffpopTb4b4b4(Te6edf3keyTb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3interface_detailsTb4b4b4[Te6edf3keyTb4b4b4] Tff7b72= Te6edf3value
T8b949e# interface discovery options
Tff7b72for Te6edf3discovery_key Tff7b72in Tb4b4b4(
Ta5d6ff"Ta5d6ffdiscoverableTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdiscovery_nameTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffannounce_intervalTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffreachable_onTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdiscovery_stamp_valueTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdiscovery_encryptTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffpublish_ifacTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflatitudeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflongitudeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffheightTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdiscovery_frequencyTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdiscovery_bandwidthTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdiscovery_modulationTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Te6edf3discovery_keyTb4b4b4)
Te6edf3location_cmd_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffapply_location_cmdTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3location_cmd_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3location_cmd_errorTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffTCPClientInterfaceTa5d6ff" Tff7b72or Tb4b4b4(
Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffBackboneInterfaceTa5d6ff"
Tff7b72and Tffa657strTb4b4b4(Te6edf3interface_detailsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffremoteTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72!= Ta5d6ff"Ta5d6ff"
Tb4b4b4)Tb4b4b4:
Te6edf3default_boot Tff7b72= Tffa657boolTb4b4b4(
Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfigTff7b72.Td2a8ffdefault_bootstrap_onlyTff7b72.Td2a8ffgetTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_context Tff7b72and Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffconfig
Tff7b72else Tff7b72FalseTb4b4b4,
Tb4b4b4)
Te6edf3ReticulumMeshChatTff7b72.Td2a8ffapply_bootstrap_only_to_interfaceTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Te6edf3default_bootTb4b4b4,
Te6edf3updating_existingTff7b72=Te6edf3allow_overwriting_interfaceTb4b4b4,
Tb4b4b4)
T8b949e# set common interface options
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffbitrateTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3interface_type Tff7b72!= Ta5d6ff"Ta5d6ffHTTPInterfaceTa5d6ff"Tb4b4b4:
Te6edf3mode_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffapply_interface_modeTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4)
Tff7b72if Te6edf3mode_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3mode_errorTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3recursive_prs_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffapply_yes_no_optionTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffrecursive_prsTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3recursive_prs_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3recursive_prs_errorTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3announces_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffapply_yes_no_optionTb4b4b4(
Te6edf3interface_detailsTb4b4b4,
Te6edf3dataTb4b4b4,
Ta5d6ff"Ta5d6ffannounces_from_internalTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3announces_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3announces_errorTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffnetwork_nameTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffpassphraseTa5d6ff"Tb4b4b4)
Te6edf3InterfaceEditorTff7b72.Td2a8ffupdate_valueTb4b4b4(Te6edf3interface_detailsTb4b4b4, Te6edf3dataTb4b4b4, Ta5d6ff"Ta5d6ffifac_sizeTa5d6ff"Tb4b4b4)
T8b949e# merge new interface into existing interfaces
Te6edf3interfaces_before_write Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_snapshotTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffI2PInterfaceTa5d6ff"Tb4b4b4:
T8b949e# I2P must be last: drop and reinsert so ConfigObj order is correct.
Te6edf3interfacesTff7b72.Td2a8ffpopTb4b4b4(Te6edf3interface_nameTb4b4b4, Tff7b72NoneTb4b4b4)
Te6edf3interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4] Tff7b72= Te6edf3interface_details
Tff7b72elseTb4b4b4:
Te6edf3interfacesTb4b4b4[Te6edf3interface_nameTb4b4b4] Tff7b72= Te6edf3interface_details
T8b949e# save config
Tff7b72if Tff7b72not Te6edf3appTff7b72.Td2a8ff_write_reticulum_configTb4b4b4(
Te6edf3rollback_interfacesTff7b72=Te6edf3interfaces_before_writeTb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6ff"Ta5d6ffFailed to write Reticulum config. Ta5d6ff"
Ta5d6ff"Ta5d6ffInterface names must not contain Ta5d6ff'Ta5d6ff[Ta5d6ff'Ta5d6ff or Ta5d6ff'Ta5d6ff]Ta5d6ff'Ta5d6ff Ta5d6ff"
Ta5d6ff"Ta5d6ff(ConfigObj section syntax).Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3allow_overwriting_interfaceTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface has been savedTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3interface_type Tff7b72== Ta5d6ff"Ta5d6ffI2PInterfaceTa5d6ff"Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6ff"Ta5d6ffI2P interface has been added as the last interface. Ta5d6ff"
Ta5d6ff"Ta5d6ffPlease restart MeshChat for these changes to take effect. Ta5d6ff"
Ta5d6ff"Ta5d6ffDo not add or reorder other interfaces afterward without Ta5d6ff"
Ta5d6ff"Ta5d6ffremoving I2P first.Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterface has been added. Please restart MeshChat for these changes to take effect.Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e# export interfaces
T8b949e# export interfaces
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/exportTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffexport_interfacesTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
T8b949e# get request data
Te6edf3selected_interface_names Tff7b72= Tff7b72None
Tff7b72tryTb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3selected_interface_names Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffselected_interface_namesTa5d6ff"Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
T8b949e# request data was not json, but we don't care
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffRequest data was not JSON: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
T8b949e# format interfaces for export
Te6edf3output Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_snapshotTb4b4b4(Tb4b4b4)
Tff7b72for Te6edf3interface_nameTb4b4b4, Te6edf3interface Tff7b72in Te6edf3interfacesTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e# skip interface if not selected
Tff7b72if Tb4b4b4(
Te6edf3selected_interface_names Tff7b72is Tff7b72not Tff7b72None
Tff7b72and Te6edf3selected_interface_names Tff7b72!= Ta5d6ff"Ta5d6ff"
Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3interface_name Tff7b72not Tff7b72in Te6edf3selected_interface_namesTb4b4b4:
Tff7b72continue
T8b949e# add interface to output
Te6edf3outputTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff[[Tffd700{Te6edf3interface_nameTffd700}Ta5d6ff]]Ta5d6ff"Tb4b4b4)
Tff7b72for Te6edf3keyTb4b4b4, Te6edf3value Tff7b72in Te6edf3interfaceTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3valueTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3outputTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Tffd700{Te6edf3keyTffd700}Ta5d6ff = Tffd700{Te6edf3valueTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3outputTff7b72.Td2a8ffappendTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
T8b949e# Handle sub-interfaces for RNodeMultiInterface
Tff7b72if Te6edf3interfaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4:
Tff7b72for Te6edf3sub_nameTb4b4b4, Te6edf3sub_config Tff7b72in Te6edf3interfaceTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3sub_name Tff7b72in Tb4b4b4{Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72continue
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3sub_configTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3outputTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff [[[Tffd700{Te6edf3sub_nameTffd700}Ta5d6ff]]]Ta5d6ff"Tb4b4b4)
Tff7b72for Te6edf3sub_keyTb4b4b4, Te6edf3sub_value Tff7b72in Te6edf3sub_configTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3outputTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Tffd700{Te6edf3sub_keyTffd700}Ta5d6ff = Tffd700{Te6edf3sub_valueTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3outputTff7b72.Td2a8ffappendTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffResponseTb4b4b4(
Te6edf3textTff7b72=Ta5d6ff"Tffea00\nTa5d6ff"Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3outputTb4b4b4)Tb4b4b4,
Te6edf3content_typeTff7b72=Ta5d6ff"Ta5d6fftext/plainTa5d6ff"Tb4b4b4,
Te6edf3headersTff7b72=Tb4b4b4{
Ta5d6ff"Ta5d6ffContent-DispositionTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffattachment; filename=meshchat_interfacesTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to export interfaces: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
T8b949e# preview importable interfaces
T8b949e# preview importable interfaces
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/import-previewTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffimport_interfaces_previewTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
T8b949e# get request data
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3config Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffconfigTa5d6ff"Tb4b4b4)
T8b949e# parse interfaces from config
Te6edf3interfaces Tff7b72= Te6edf3InterfaceConfigParserTff7b72.Td2a8ffparseTb4b4b4(Te6edf3configTb4b4b4)
T8b949e# I2P must not be imported from files, so hide it from the picker.
Te6edf3interfaces Tff7b72= Tb4b4b4[
Te6edf3iface
Tff7b72for Te6edf3iface Tff7b72in Te6edf3interfaces
Tff7b72if Tffa657strTb4b4b4(Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72!= Ta5d6ff"Ta5d6ffI2PInterfaceTa5d6ff"
Tb4b4b4]
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4: Te6edf3interfacesTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to parse config file: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
T8b949e# import interfaces from config
T8b949e# import interfaces from config
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/reticulum/interfaces/importTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffimport_interfacesTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
T8b949e# get request data
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3config Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffconfigTa5d6ff"Tb4b4b4)
Te6edf3selected_interface_names Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffselected_interface_namesTa5d6ff"Tb4b4b4)
T8b949e# parse interfaces from config
Te6edf3interfaces Tff7b72= Te6edf3InterfaceConfigParserTff7b72.Td2a8ffparseTb4b4b4(Te6edf3configTb4b4b4)
T8b949e# find selected interfaces
Te6edf3selected_interfaces Tff7b72= Tb4b4b4[
Te6edf3interface
Tff7b72for Te6edf3interface Tff7b72in Te6edf3interfaces
Tff7b72if Te6edf3interfaceTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4] Tff7b72in Te6edf3selected_interface_names
Tb4b4b4]
T8b949e# convert interfaces to object
Te6edf3interface_config Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72for Te6edf3interface Tff7b72in Te6edf3selected_interfacesTb4b4b4:
T8b949e# add interface and keys/values
Te6edf3interface_name Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffsanitize_interface_section_nameTb4b4b4(
Te6edf3interfaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3interface_nameTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffImported interface is missing a valid nameTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3interface_configTb4b4b4[Te6edf3interface_nameTb4b4b4] Tff7b72= Tb4b4b4{Tb4b4b4}
Tff7b72for Te6edf3keyTb4b4b4, Te6edf3value Tff7b72in Te6edf3interfaceTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3interface_configTb4b4b4[Te6edf3interface_nameTb4b4b4]Tb4b4b4[Te6edf3keyTb4b4b4] Tff7b72= Te6edf3value
T8b949e# unset name which isn't part of the config
Tff7b72del Te6edf3interface_configTb4b4b4[Te6edf3interface_nameTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]
T8b949e# force imported interface to be enabled by default
Te6edf3interface_configTb4b4b4[Te6edf3interface_nameTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffinterface_enabledTa5d6ff"Tb4b4b4] Tff7b72= Ta5d6ff"Ta5d6fftrueTa5d6ff"
T8b949e# remove enabled config value in favour of interface_enabled
Tff7b72if Ta5d6ff"Ta5d6ffenabledTa5d6ff" Tff7b72in Te6edf3interface_configTb4b4b4[Te6edf3interface_nameTb4b4b4]Tb4b4b4:
Tff7b72del Te6edf3interface_configTb4b4b4[Te6edf3interface_nameTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffenabledTa5d6ff"Tb4b4b4]
Te6edf3iface_body Tff7b72= Te6edf3interface_configTb4b4b4[Te6edf3interface_nameTb4b4b4]
Te6edf3iface_type Tff7b72= Te6edf3iface_bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3iface_type Tff7b72== Ta5d6ff"Ta5d6ffI2PInterfaceTa5d6ff" Tff7b72or Te6edf3i2p_supportTff7b72.Td2a8ffis_i2p_interfaceTb4b4b4(
Te6edf3iface_bodyTb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3i2p_supportTff7b72.Td2a8ffMSG_IMPORT_FORBIDDENTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3import_option_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffsanitize_imported_rns_optionsTb4b4b4(
Te6edf3iface_body
Tb4b4b4)
Tff7b72if Te6edf3import_option_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Te6edf3import_option_errorTb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3iface_type Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffRNodeInterfaceTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffRNodeIPInterfaceTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3freq Tff7b72= Te6edf3iface_bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3freq Tff7b72is Tff7b72not Tff7b72None Tff7b72and Te6edf3freq Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3iface_bodyTb4b4b4[Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4] Tff7b72= Tb4b4b4(
Te6edf3InterfaceEditorTff7b72.Td2a8ffcoerce_rnode_frequency_hzTb4b4b4(Te6edf3freqTb4b4b4)
Tb4b4b4)
Te6edf3txpower Tff7b72= Te6edf3iface_bodyTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3txpower Tff7b72is Tff7b72not Tff7b72None Tff7b72and Te6edf3txpower Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3txpower_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffvalidate_rnode_txpowerTb4b4b4(
Te6edf3txpowerTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3txpower_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6fffTa5d6ff'Ta5d6ffInterface Ta5d6ff"Tffd700{Te6edf3interface_nameTffd700}Ta5d6ff"Ta5d6ff: Tffd700{Te6edf3txpower_errorTffd700}Ta5d6ff'
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3iface_bodyTb4b4b4[Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffnormalize_rnode_txpowerTb4b4b4(
Te6edf3txpower
Tb4b4b4)
Tff7b72elif Te6edf3iface_type Tff7b72== Ta5d6ff"Ta5d6ffRNodeMultiInterfaceTa5d6ff"Tb4b4b4:
Tff7b72for Te6edf3sub_keyTb4b4b4, Te6edf3sub Tff7b72in Tffa657listTb4b4b4(Te6edf3iface_bodyTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3subTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3freq Tff7b72= Te6edf3subTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3freq Tff7b72is Tff7b72not Tff7b72None Tff7b72and Te6edf3freq Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3subTb4b4b4[Ta5d6ff"Ta5d6fffrequencyTa5d6ff"Tb4b4b4] Tff7b72= Tb4b4b4(
Te6edf3InterfaceEditorTff7b72.Td2a8ffcoerce_rnode_frequency_hzTb4b4b4(Te6edf3freqTb4b4b4)
Tb4b4b4)
Te6edf3txpower Tff7b72= Te6edf3subTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3txpower Tff7b72is Tff7b72not Tff7b72None Tff7b72and Te6edf3txpower Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3txpower_error Tff7b72= Te6edf3InterfaceEditorTff7b72.Td2a8ffvalidate_rnode_txpowerTb4b4b4(
Te6edf3txpowerTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3txpower_error Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6fffTa5d6ff'Ta5d6ffInterface Ta5d6ff"Tffd700{Te6edf3interface_nameTffd700}Ta5d6ff"Ta5d6ff Ta5d6ff'
Ta5d6fffTa5d6ff'Ta5d6ffsub-interface Ta5d6ff"Tffd700{Te6edf3sub_keyTffd700}Ta5d6ff"Ta5d6ff: Ta5d6ff'
Ta5d6fffTa5d6ff"Tffd700{Te6edf3txpower_errorTffd700}Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff422Tb4b4b4,
Tb4b4b4)
Te6edf3subTb4b4b4[Ta5d6ff"Ta5d6fftxpowerTa5d6ff"Tb4b4b4] Tff7b72= Tb4b4b4(
Te6edf3InterfaceEditorTff7b72.Td2a8ffnormalize_rnode_txpowerTb4b4b4(
Te6edf3txpowerTb4b4b4,
Tb4b4b4)
Tb4b4b4)
T8b949e# update reticulum config with new interfaces
Te6edf3interfaces_before_write Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_snapshotTb4b4b4(Tb4b4b4)
Te6edf3interfaces Tff7b72= Te6edf3appTff7b72.Td2a8ff_get_interfaces_sectionTb4b4b4(Tb4b4b4)
Te6edf3interfacesTff7b72.Td2a8ffupdateTb4b4b4(Te6edf3interface_configTb4b4b4)
Tff7b72if Tff7b72not Te6edf3appTff7b72.Td2a8ff_write_reticulum_configTb4b4b4(
Te6edf3rollback_interfacesTff7b72=Te6edf3interfaces_before_writeTb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Tb4b4b4(
Ta5d6ff"Ta5d6ffFailed to write Reticulum config. Ta5d6ff"
Ta5d6ff"Ta5d6ffInterface names must not contain Ta5d6ff'Ta5d6ff[Ta5d6ff'Ta5d6ff or Ta5d6ff'Ta5d6ff]Ta5d6ff'Ta5d6ff Ta5d6ff"
Ta5d6ff"Ta5d6ff(ConfigObj section syntax).Ta5d6ff"
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInterfaces imported successfullyTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffFailed to import interfaces: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff500Tb4b4b4,
Tb4b4b4)
T8b949e# handle websocket clients
T8b949e# get interface stats
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/interface-statsTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffinterface_statsTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffinterface_statsTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ff_get_interface_stats_payloadTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e# get path table
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────